8.0
JIT
JIT简介:PHP 8新特性之JIT简介 - 风雪之隅
值得被提起的则是JIT新的特性,它会将PHP代码转换为传统的机器码,而并非通过zend虚拟机来运行,这样大大的增加了运行速度,
但并不向下兼容,这意味着你不能通过像PHP5升级到PHP7那样获得该特性。
JIT作为PHP底层编译引擎,对于PHP8的性能贡献是非常之大,不过对于常规WEB应用来说,优势不明显,但仍然是非常的高大上特性,是PHP8的扛鼎之作。
PHP 8 引入了两个即时编译引擎。 Tracing JIT 在两个中更有潜力,它在综合基准测试中显示了三倍的性能, 并在某些长时间运行的程序中显示了 1.5-2 倍的性能改进。
典型的应用性能则和 PHP 7.4 不相上下。
关于 JIT 对 PHP 8 性能的贡献

JIT可以通过php.ini去设置,例如这样
opcache.jit=on # on 代表打开,则off代表关闭
注解
PHP8版本彻底把注解扶正,当然在这之前像 Symfony,hyperf通过php-parser加入注解的使用方法,但这毕竟不属于PHP8内核真正的部分,在PHP8的版本中,但依旧需要反射去获取到注解部分,看来注解在PHP的历史长河中还是需要继续不断完善的。
new ReflecationPropertyclass, "id";
PHP7
之前定义注解路由可能需要使用:
class PostsController
{
/**
* @Route("/api/posts/{id}", methods={"GET"})
*/
public function get($id) { /* ... */ }
}
PHP8
现在你可以直接用PHP的注解语法来定义,并通过反射直接获取
class PostsController
{
#[Route("/api/posts/{id}", methods: ["GET"])]
public function get($id) { /* ... */ }
}
构造器属性提升
PHP7
class User
{
public $username;
public $phone;
public $sex;
public function __construct(
$username, $phone, $sex
) {
$this->username = $username;
$this->phone = $phone;
$this->sex = $sex;
}
}
PHP8
class User
{
public function __contruct(
public string $username = "zhangsan",
public string $phone = "110110";
public string $sex = "男"
){}
}
命名参数
当我们创建一个函数时,例如
function roule($name, $controller, $model){
// ... code
}
PHP7
在调用这个函数时,我们需要顺序输入参数
roule("user/login","UserController","login");
PHP8
roule(name:"user/login",controller:"UserController",model:"login");
因为可以需要输入参数名来区分传入的字段,那么在一些函数中,类比中间某项这段需要默认值,那我们就可以跳过这个字段
function roule($name,$controller="UserController",$model){
// ... code
}
roule(name:"user/login",model:"login");
当然也可以以传统方式与其相结合
roule("user/login",model:"login");
联合类型、mixed类型
PHP7
function create() : bool
class Number {
/** @var int|float */
private $number;
/**
* @param float|int $number
*/
public function __construct($number) {
$this->number = $number;
}
}
new Number('NaN'); // Ok
PHP8
function create() : bool|string
当然在传参时也可以这样做
function create(bool|string $userId)
并且也可以设置类型NULL和TRUE,FALSE了。
class Number {
public function __construct(
private int|float $number
) {}
}
new Number('NaN'); // TypeError
新的 mixed类型
mixed本身是以下类型之一:
- array
- bool
- callable
- int
- float
- null
- object
- resource
- string
注意,mixed也可以用作参数或属性类型,而不仅仅是返回类型。
另外由于mixed已经包含null,因此不允许将其设置为nullable。以下内容将触发错误:
function bar(): ?mixed {} // Fatal error: Mixed types cannot be nullable, null is already part of the mixed type.
match表达式
在以前我们可能会经常使用switch做值转换类的工作
PHP7
switch ($input) {
case "true":
$result = 1;
break;
case "false":
$result = 0;
break;
case "null":
$result = NULL;
break;
default:
$result = false;
break;
}
PHP8
$result = match($input) {
"true" => 1,
"false" => 0,
"null" => NULL,
default => false
};
-----------------------------------------------------------------
// match的多个条件也可以写在一起
$result = match($input) {
"true", "on" => 1,
"false", "off" => 0,
"null", "empty", "NaN" => NULL,
default => false
};
需要注意的和switch不太一样的是,以前我们用switch可能会经常遇到这种诡异的问题:
$input = "2 person";
switch ($input) {
case 2:
echo "bad";
break;
}
你会发现,bad竟然被输出了,这是因为switch使用了宽松比较(==)。match使用的是严格比较(===),就是值和类型都要完全相等。
还有就是,当input并不能被match中的所有条件满足的时候,match会抛出一个UnhandledMatchError exception:
$input = "false";
$result = match($input) {
"true" => 1,
};
// 会得到: Fatal error: Uncaught UnhandledMatchError: Unhandled match value of type string
另外还是要说明,match是关键字,也就是从PHP8开始它不能出现在namespace或者类名中,如果你的项目中有用match作为类名的:
class Match {}
Nullsafe 运算符
PHP7
$country = null;
if ($session !== null) {
$user = $session->user;
if ($user !== null) {
$address = $user->getAddress();
if ($address !== null) {
$country = $address->country;
}
}
}
PHP8
$country = $session?->user?->getAddress()?->country;
字符串与数字的比较更符合逻辑
PHP7
0 == 'foobar' // true
PHP8
0 == 'foobar' // false
内部函数类型错误的一致性
PHP7
strlen([]); // Warning: strlen() expects parameter 1 to be string, array given
array_chunk([], -1); // Warning: array_chunk(): Size parameter expected to be greater than 0
var_dump(implode([], '')); // string(0) ""
PHP8
strlen([]); // TypeError: strlen(): Argument #1 ($str) must be of type string, array given
array_chunk([], -1); // ValueError: array_chunk(): Argument #2 ($length) must be greater than 0
implode([], ''); // string(0) ""
str_contains()、str_starts_with()、str_ends_with()
PHP7
if (strpos('string with lots of words', 'words') !== false) { /* … */ }
PHP8
if (str_contains('string with lots of words', 'words')) { /* … */ }
str_starts_with('haystack', 'hay'); // true
str_ends_with('haystack', 'stack'); // true
可以在对象上使用::class
一个小而有用的新特性:现在可以对对象使用::class,它的工作方式与 get_class() 相同。
PHP7
$foo = new Foo();
var_dumpclass; // Fatal error: Dynamic class names are not allowed in compile-time ::class
PHP8
$foo = new Foo();
var_dumpclass; // Foo
traits 中的抽象方法改进
Traits 可以指定抽象方法,这些方法必须由使用它们的类实现。在PHP8,必须保持一致的方法定义,包括参数类型和返回类型。
trait MyTrait
{
abstract private function neededByTheTrait(): string;
public function doSomething()
{
return strlen($this->neededByTheTrait());
}
}
class TraitUser
{
use MyTrait;
// This is allowed:
private function neededByTheTrait(): string
{
}
// This is forbidden (incorrect return type)
private function neededByTheTrait(): stdClass
{
}
// This is forbidden (non-static changed to static)
private static function neededByTheTrait(): string
{
}
}